﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace HF
{
    public static partial class HExpr
    {
        /// <summary>
        /// 通过 <see cref="Expression"/> 动态编译读写对象属性的匿名方法
        /// </summary>
        public static partial class Property
        {
            /// <summary>
            /// 读取属性
            /// </summary>
            public static class Reader
            {
                private static readonly ConcurrentDictionary<Type, Lazy<(Func<object, object>, Delegate)>> _MapRead = new();

                private static readonly ConcurrentDictionary<Type[], Lazy<(Func<object, object[], object>, Delegate)>> _MapReadIndex = new();

                /// <summary>
                /// 编译静态访问属性匿名函数
                /// </summary>
                /// <param name="returnType"></param>
                /// <param name="mem"></param>
                /// <returns></returns>
                private static Type CompliceReadStatic(Type returnType, MemberInfo mem, out Func<object, object> read, out Delegate fastRead)
                {
                    mem.CheckArgumentNull(nameof(mem));
                    Expression memExpr = mem.MemberType switch
                    {
                        MemberTypes.Property => Expression.Field(null, (FieldInfo)mem),
                        MemberTypes.Field => Expression.Property(null, (PropertyInfo)mem),
                        _ => throw new Exception(),
                    };

                    {
                        var lambda = Expression.Lambda(memExpr, $"Fun<{returnType.GetTypeFullName()}>", null);
                        fastRead = lambda.Compile();
                    }

                    {
                        //如果对象是值类型就强行转换成 object            
                        if (returnType.IsValueType)   //((object)obj.property);
                        {
                            memExpr = Expression.Convert(memExpr, typeof(object));
                        }

                        var lambdaExpr1 = Expression.Lambda<Func<object, object>>(
                            memExpr,
                            "Fun<object, object>",
                            new ParameterExpression[] { Expression.Parameter(typeof(object)) });

                        read = lambdaExpr1.Compile();
                    }

                    return mem.DeclaringType;
                }

                /// <summary>
                /// 编译实体访问属性匿名函数
                /// </summary>
                /// <param name="returnType"></param>
                /// <param name="mem"></param>
                /// <returns></returns>
                private static Type CompliceRead(Type returnType, MemberInfo mem, out Func<object, object> read, out Delegate fastRead)
                {
                    var selfType = mem.DeclaringType;
                    {
                        const string C_FunName = "Fun<object, object>";
                        var p1Expr = Expression.Parameter(typeof(object), "p1");
                        var p1C = selfType.IsValueType ?
                            Expression.Unbox(p1Expr, selfType) :
                            Expression.Convert(p1Expr, selfType);

                        Expression memExpr = Expression.PropertyOrField(p1C, mem.Name);

                        if (returnType.IsValueType)
                        {
                            memExpr = Expression.Convert(memExpr, typeof(object));
                        }

                        var lambdaExpr = Expression.Lambda<Func<object, object>>(memExpr, C_FunName, new[] { p1Expr });
                        read = lambdaExpr.Compile();
                    }

                    {
                        var isValue = selfType.IsValueType;

                        string funName = isValue ?
                            $"FunRef<{GetTypeFullName(selfType)}, {returnType.Name}>" :
                            $"Fun<{GetTypeFullName(selfType)}, {returnType.Name}>";

                        var parType = isValue ? selfType.MakeByRefType() : selfType;
                        var p1Expr = Expression.Parameter(parType, "p1");
                        var memExpr = Expression.PropertyOrField(p1Expr, mem.Name);

                        var et = isValue ?
                            typeof(FuncRef<,>).MakeGenericType(selfType, returnType) :
                            typeof(Func<,>).MakeGenericType(selfType, returnType);

                        var lambdaExpr = Expression.Lambda(et, memExpr, funName, new[] { p1Expr });
                        fastRead = lambdaExpr.Compile();
                    }
                    return selfType;
                }

                private static Type CompliceReadIndex(PropertyInfo pro, out Func<object, object[], object> read, out Delegate fastRead)
                {
                    var indexArgs = pro.GetIndexParameters().Select(v => v.ParameterType).ToArray();
                    var selfType = pro.DeclaringType;
                    {
                        const string C_FunName = "Fun<object, object[], object>";

                        //参数1 （object param_object）
                        var p1Expr = Expression.Parameter(typeof(object), "p1");

                        //参数2 （object[] params)
                        var p2Expr = Expression.Parameter(typeof(object[]), "p2");

                        //将 参数1 类型强制转换为 属性类型
                        var p1CExpr = selfType.IsValueType ?
                            Expression.Unbox(p1Expr, selfType) :
                            Expression.Convert(p1Expr, selfType);

                        //将 参数2 类型强制转换为对应的类型
                        var p2CExpr = new Expression[indexArgs.Length];
                        for (int i = 0; i < indexArgs.Length; i++)
                        {
                            p2CExpr[i] = ConvertExpr(
                                Expression.ArrayIndex(p2Expr, Expression.Constant(i, typeof(int))),
                                indexArgs[i]);
                        }

                        Expression bodyExpr = Expression.MakeIndex(p1CExpr, pro, p2CExpr);
                        if (pro.PropertyType.IsValueType)
                        {
                            bodyExpr = Expression.Convert(bodyExpr, typeof(object));
                        }

                        var ps = new ParameterExpression[] { p1Expr, p2Expr };
                        var lambdaExpr = Expression.Lambda<Func<object, object[], object>>(bodyExpr, C_FunName, ps);
                        read = lambdaExpr.Compile();
                    }

                    {
                        bool isValueType = selfType.IsValueType;
                        var p1Expr = isValueType ?
                            Expression.Parameter(selfType.MakeByRefType(), "p1") :
                            Expression.Parameter(selfType, "p1");

                        var p2Expr = indexArgs.Select(v => Expression.Parameter(v)).ToArray();

                        var types = new Type[] { selfType }
                            .Concat(indexArgs)
                            .Concat(new Type[] { pro.PropertyType })
                            .ToArray();

                        var strParam = types
                            .Select(v => v.GetTypeFullName())
                            .Aggregate((u, d) => $"{u}, {d}");

                        string funName = isValueType ? $"FuncRef<{strParam}>" : $"Func<{strParam}>";
                        Type et = GetDelegate(types, false, isValueType);
                        Expression bodyExpr = Expression.MakeIndex(p1Expr, pro, p2Expr);
                        var paramsExpr = new ParameterExpression[] { p1Expr }.Concat(p2Expr).ToArray();
                        var lambdaExpr = Expression.Lambda(et, bodyExpr, funName, paramsExpr);
                        fastRead = lambdaExpr.Compile();
                    }
                    return selfType;
                }

                /// <summary>
                /// 动态编译获取类型 <paramref name="source"/>.<paramref name="proName"/> 的匿名函数
                /// </summary>
                /// <param name="source">属性所属的对象类型</param>
                /// <param name="proName">属性或字段的名称</param>
                /// <param name="read">
                /// 返回动态编译读取属性的 <see cref="Func{Object, Object}"/> 匿名函数
                /// <list type="bullet">
                /// <item>参数 : <see cref="object"/> 待读取的实体对象,★★★如果是静态属性参数随便给★★★</item>
                /// <item>返回 : <see cref="object"/> 返回的对象</item>
                /// </list>
                /// </param>
                /// <param name="fastRead">
                /// 返回动态编译读取属性的 <see cref="Delegate"/> 函数, 性能接近直接访问属性
                /// <list type="bullet">
                /// <item>因参数的类型不确定性需要强制转换 <paramref name="fastRead"/> 后调用</item>
                /// <item>输出 fastRead.Method.Name 可以查看委托定义格式</item>
                /// <item>为提高性能,(非 ref struct、非静态) 的结构体返回的引用委托 <see cref="FuncRef{T1, TResult}"/></item>
                /// </list>
                /// </param>
                /// <returns></returns>
                public static Type Complice(Type source, string proName, out Func<object, object> read, out Delegate fastRead)
                {
                    var result = _MapRead.GetOrAdd(source, v => newLazy(v));
                    read = result.Value.Item1;
                    fastRead = result.Value.Item2;
                    return source;
                    Lazy<(Func<object, object>, Delegate)> newLazy(Type v) =>
                        new(() =>
                        {
                            var memInfo = GetPropertyField(source, proName, false, out var isStatic, out var returnType);
                            var r = isStatic ?
                                CompliceReadStatic(returnType, memInfo, out var rs1, out var rs2) :
                                CompliceRead(returnType, memInfo, out rs1, out rs2);
                            return (rs1, rs2);
                        }
                        , LazyThreadSafetyMode.PublicationOnly);
                }

                /// <summary>
                /// 获取读取属性索引的函数
                /// </summary>
                /// <param name="source">属性所属的实体类型</param>
                /// <param name="paramTypes">索引参数类型集合</param>
                /// <param name="read">
                /// 返回读取索引的 <see cref="Func{T1, T2, TResult}"/> 函数
                /// <list type="bullet">
                /// <item>参数1 : <see cref="object"/> 待读取的实体对象</item>
                /// <item>参数2 : <see cref="object"/>[] 索引可以重载,用数组兼容</item>
                /// <item>返回 : <see cref="object"/> 对象</item>
                /// </list>
                /// </param>
                /// <param name="fastRead">
                /// 返回高效读取索引的 <see cref="Delegate"/> 函数, 性能接近直接访问属性
                /// <list type="bullet">
                /// <item>因参数的类型、个数的不确定性需要强制转换 <paramref name="fastRead"/> 后调用</item>
                /// <item>输出 fastRead.Method.Name 可以查看委托定义格式</item>
                /// <item>为提高性能,非 ref struct 的结构体返回的引用委托 <see cref="ActionRef{T1}"/> 或 <see cref="FuncRef{T1, TResult}"/></item>
                /// </list>
                /// </param>
                /// <returns></returns>
                public static Type Complice(Type source, Type[] paramTypes, out Func<object, object[], object> read, out Delegate fastRead)
                {
                    source.CheckArgumentNull(nameof(source));
                    paramTypes.CheckArgumentNull(nameof(paramTypes));
                    if (paramTypes.Length == 0)
                    {
                        throw new ArgumentOutOfRangeException(nameof(paramTypes));
                    }

                    var key = new Type[] { source }.Concat(paramTypes).ToArray();
                    var result = _MapReadIndex.GetOrAdd(key, v => newLazy(v[0]));
                    read = result.Value.Item1;
                    fastRead = result.Value.Item2;
                    return source;
                    Lazy<(Func<object, object[], object>, Delegate)> newLazy(Type v) =>
                        new(() =>
                        {
                            var pro = GetIndexProperty(source, paramTypes, false);
                            CompliceReadIndex(pro, out var rs1, out var rs2);
                            return (rs1, rs2);
                        }
                        , LazyThreadSafetyMode.PublicationOnly);
                }
            }
        }
    }
}
